<!doctype html>
<html lang="en">
<head>
    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
    <title>仿重力小球</title>
    <style>
        body {
            padding: 0;
            margin: 0;
            overflow: hidden;
        }

        #cas {
            display: block;
        }
    </style>
</head>
<body>
<div>
    <canvas id='cas' width="1200" height="600" style="background-color:#000">浏览器不支持canvas</canvas>
</div>
<script src="../public/stats.min.js"></script>
<script charset="utf-8">
    window.onload = function () {
        var stats = new Stats();
        stats.setMode(0);
        stats.domElement.style.position = 'absolute';
        stats.domElement.style.right = '0px';
        stats.domElement.style.top = '0px';
        document.body.appendChild( stats.domElement );
        canvas = document.getElementById("cas");
        ctx = canvas.getContext("2d");
        canvas.width = window.innerWidth;
        canvas.height = window.innerHeight;
        //球的半径，      重力加速度，  左右碰撞的加速度    存放所有的球，弹性
        ballRadius = 12, g = 9.8 , mocali = 0.5, balls = [], collarg = 0.8;
        //显示的宽度决定了球的活跃度,视觉上感受更好
        pxpm = canvas.width / 20;   //px/m
        //构建100个球
        for (var i = 0; i < 100; i++) {
            //  ballRadius, canvas.width - ballRadius为了使球整个完整的都在画布中
            var ball = new Ball(getRandom(ballRadius, canvas.width - ballRadius), getRandom(ballRadius, canvas.height - ballRadius), ballRadius, "rgba(" + parseInt(Math.random() * 255) + "," + parseInt(Math.random() * 255) + "," + parseInt(Math.random() * 255) + " , 1)");
            //存储所有的球
            balls.push(ball);
        }
        //开始你的表演
        animate();

        canvas.onclick = function (event) {
            event = event || window.event;
            var x = event.clientX + document.body.scrollLeft + document.documentElement.scrollLeft - canvas.offsetLeft;
            var y = event.clientY + document.body.scrollTop + document.documentElement.scrollTop - canvas.offsetTop;

            balls.forEach(function (b) {
                b.vx = (x - b.x) / 40; //初速度 m/s
                b.vy = (y - b.y) / 40;
            });
        };

        //产生a到b之间的随机数
        function getRandom(a, b) {
            return Math.random() * (b - a) + a;
        }

        //动画函数
        function animate() {
            //清空画布
            ctx.clearRect(0, 0, canvas.width, canvas.height)
            //  保存当前状态
            ctx.save();
            //设置画布填充颜色
            ctx.fillStyle = "rgba(255,255,255,0.2)";
            //填充整个屏幕
            ctx.fillRect(0, 0, canvas.width, canvas.height)
            //  恢复画布
            ctx.restore();

            var t = 16 / 1000;//requestAnimationFrame调用函数执行的时间在16.7ms~33.3ms之间
            collision();
            balls.forEach(function (b) {
                b.run(t);
            });
            stats.update();
            if ("requestAnimationFrame" in window) {
                requestAnimationFrame(animate);
            } else if ("webkitRequestAnimationFrame" in window) {
                webkitRequestAnimationFrame(animate);
            } else if ("msRequestAnimationFrame" in window) {
                msRequestAnimationFrame(animate);
            } else if ("mozRequestAnimationFrame" in window) {
                mozRequestAnimationFrame(animate);
            }
        }
    };

    //碰撞
    function collision() {
        //当前的球和所有的球进行碰撞检测
        for (var i = 0; i < balls.length; i++) {
            for (var j = 0; j < balls.length; j++) {
                //检测碰撞的两个球
                var b1 = balls[i], b2 = balls[j];
                //排除球自身
                if (b1 !== b2) {
                    //
                    var rc = Math.sqrt(Math.pow(b1.x - b2.x, 2) + Math.pow(b1.y - b2.y, 2));
                    //碰撞检测,当两球坐标的距离小于两球的半径的时候，即为碰撞到了
                    if (Math.ceil(rc) < (b1.radius + b2.radius + 2)) {

                        //获得碰撞后速度的增量
                        var ax = ((b1.vx - b2.vx) * Math.pow((b1.x - b2.x), 2) + (b1.vy - b2.vy) * (b1.x - b2.x) * (b1.y - b2.y)) / Math.pow(rc, 2);
                        var ay = ((b1.vy - b2.vy) * Math.pow((b1.y - b2.y), 2) + (b1.vx - b2.vx) * (b1.x - b2.x) + (b1.y - b2.y)) / Math.pow(rc, 2);
                        // console.log("获得碰撞后速度的增量x", b1.vx, b2.vx );
                        // console.log("获得碰撞后速度的增量y", b1.vy, b2.vy);
                        // return
                        //给与小球新的速度
                        b1.vx = (b1.vx - ax) * collarg;
                        b1.vy = (b1.vy - ay) * collarg;
                        b2.vx = (b2.vx + ax) * collarg;
                        b2.vy = (b2.vy + ay) * collarg;

                        //获取两球斜切位置并且强制扭转
                        var clength = ((b1.radius + b2.radius + 2) - rc) / 2;
                        var cx = clength * (b1.x - b2.x) / rc;
                        var cy = clength * (b1.y - b2.y) / rc;
                        b1.x = b1.x + cx;
                        b1.y = b1.y + cy;
                        b2.x = b2.x - cx;
                        b2.y = b2.y - cy;
                    }
                }
            }
        }
    }

    var Ball = function (x, y, r, color) {
        this.x = x;//新的x
        this.y = y;//新的y
        this.oldx = x;//旧的x
        this.oldy = y;//旧的y
        this.vx = 0;//x向量
        this.vy = 0;//y向量
        this.radius = r;//半径
        this.color = color;//颜色
        this.candrod = true;//TODO 是否重新绘制
    };

    Ball.prototype = {
        paint: function () {
            ctx.beginPath();
            ctx.arc(this.x, this.y, this.radius - 1, 0, Math.PI * 2);
            ctx.fillStyle = this.color;
            ctx.fill();
            this.moving = false;
        },
        run: function (t) {
            if (!this.candrod) {
                return this.paint();
            }

            this.oldx = this.x;
            this.oldy = this.y;

            this.vx += this.vx > 0 ? -mocali * t : mocali * t;
            this.vy = this.vy + g * t;

            this.x += t * this.vx * pxpm ;
            this.y += t * this.vy * pxpm;
            //上下边界的情况处理
            if (this.y > canvas.height - ballRadius || this.y < ballRadius) {
                this.y = this.y < ballRadius ? ballRadius : (canvas.height - ballRadius);//入射点和反弹点一致
                this.vy = -this.vy * collarg//向量取反即可
            }
            //左右边界的情况处理
            if (this.x > canvas.width - ballRadius || this.x < ballRadius) {
                this.x = this.x < ballRadius ? ballRadius : (canvas.width - ballRadius);
                this.derectionX = !this.derectionX;
                this.vx = -this.vx * collarg;
            }

            this.paint();
        },
    }

</script>
</body>
</html>